﻿using System;
using System.Linq;
using System.Linq.Expressions;

namespace Ebt.Linq
{
	public class InterceptingQuery<TItem> : Query<TItem>
	{
		public InterceptingQuery(IQueryable<TItem> underlyingQuery, params Func<Expression, Expression>[] visitors)
			: base(new InterceptingProvider(underlyingQuery, visitors), underlyingQuery.Expression)
		{
		}
		
		private class InterceptingProvider : QueryProvider
		{
			private readonly IQueryable<TItem> _underlyingQuery;
			private readonly Func<Expression, Expression>[] _visitors;

			public InterceptingProvider(IQueryable<TItem> underlyingQuery, params Func<Expression, Expression>[] visitors)
			{
				if (underlyingQuery == null) throw new ArgumentNullException("underlyingQuery");
				if (visitors == null) throw new ArgumentNullException("visitors");

				_underlyingQuery = underlyingQuery;
				_visitors = visitors;
			}

			public override string GetQueryText(Expression expression)
			{
				return expression.ToString();
			}

			public override object Execute(Expression expression)
			{
				var fixedExpression = Intercept(expression);
				return _underlyingQuery.Provider.CreateQuery(fixedExpression);
			}

			public override S Execute<S>(Expression expression)
			{
				var fixedExpression = Intercept(expression);
				return _underlyingQuery.Provider.Execute<S>(fixedExpression);
			}

			private Expression Intercept(Expression expression)
			{
				return (from visitor in _visitors select visitor).Aggregate(expression, (e, v) => v(e));
			}
		}
	}
}